<?php

/*
 * этапы работы
 * 1. сохранение/получение промежуточного рез-та в сущность
 * 2. фильтрация
 * 3. валидация
 * 4. визуализация
 * 5. вернуть значение
 */

namespace Pinenut;

use Pinenut\Request;
use Pinenut\Form\Model;
use Pinenut\Form\Element;

class Form
{
	protected $formModel = null;

	protected $sessionId;

	protected $formId;

	/**
	 * @var Pinenut\Form\Element[]
	 */
	protected $elements;

	protected $formConfig;

	protected $partial;

	protected $defaultFormRender = 'forms.render.form';

	public function __construct($sessionId, $formId, $formConfig, $data = array())
	{
		$this->sessionId = $sessionId;
		$this->formId = $formId;
		$this->formConfig = $formConfig;

		// создаю массив элементов по конфе
		foreach ($formConfig['elements'] as $elementName => $elementConfig) {
			$this->elements[$elementName] = new Element(array_merge($elementConfig, array('name' => $elementName)));
		}

		$formModel = $this->getModel($data);
		foreach ( ( array ) $formModel->formFields as $elementName => $elementValue ) {
			if (empty ( $this->elements [$elementName] ) || ! is_object ( $this->elements [$elementName] )) {
				continue;
			}
			$this->elements [$elementName]->setValue ( $elementValue );
		}
	}

	public function getModel($data = array())
	{
		if (null === $this->formModel) {
			// ?? класс FormModel жёстко прописан
			$this->formModel = Model::find()->active(true)->where('formId', $this->getFullFormId())->one();

			if (null === $this->formModel) {
				$this->formModel = new Model();
				$this->formModel->formId = $this->getFullFormId();
//				$this->formModel->formFields = $data;
				$this->formModel->save();
			}
		}

		return $this->formModel;
	}

	protected function getFullFormId()
	{
		return $this->sessionId . '.' . $this->formId;
	}

	public function __toString()
	{
		return $this->render();
	}

	/**
	 * Хелпер для вызова действия в контексте текущего
	 * TODO: оформить в подключаемый хелпер (в виде trait)
	 *
	 * @param string $action имя действия
	 * @param array $params	параметры вызова
	 * @param string|null $view имя шаблона для этого действия
	 * @return \Pinenut\Response
	 */
	function action($action, array $params = array(), $view = null, $deferred = true)
	{
		$dispatcher = \Application::$DispatcherServiceClass;

		//$action = $this->prepareActionName($action);

		$response = $dispatcher::action(new Request($action, $params, $view, $deferred));
		return $response;
	}

	public function render($elementsList = null)
	{
		/*
		 * 1. выбираем элементы списка и все их подчинённые элементы
		 * 2. добавляем признак partial, если частичная перегенерация
		 * 3. отдаём fomrConfig
		 */
		$params = $this->formConfig;
		$params['elements'] = $this->elements;
		$params['partial'] = $this->partial;
		
		return $this->action($this->defaultFormRender, $params, $this->getStyle())->__toString();
	}
	
	public function getStyle()
	{
		return isset($this->formConfig['style']) ? $this->formConfig['style'] : null;
	}

	public function setElements($elementsConfig)
	{
		$this->formConfig['elements'] = $elementsConfig;
	}

// 	public function __get($name)
// 	{
// 		if ('elements' == $name) {
// 			if (empty($this->formConfig['elements'])) {
// 				$this->formConfig['elements'];
// 			}
// 			return $this->formConfig['elements'];

// 		} else if (!empty($this->formConfig['elements'][$name]['value']))  {
// 			return $this->formConfig['elements'][$name]['value'];
// 		}
// 	}

// 	public function __set($name, $value)
// 	{
// 		if ('elements' == $name) {
// 			$this->formConfig['elements'] = $value;
// 		} else if (!empty($this->formConfig['elements'][$name]))  {
// 			$this->formConfig['elements'][$name]['value'] = $value;
// 		}
// 	}

	public function process($fieldsData, $partial = false)
	{
		/**
		 * 1. ищем данные по форме, если не находим, то массив дефолтных данных и накладываем пришедший массив данных,
		 * сохраняем данные
		 * 2. фильтруем весь массив
		 * 3. для подчинёных полей получаем данные мастер полей
		 * 4. валидация
		 */

		$this->partial = $partial;
//		$fieldsData = (array)$fieldsData;
		// 1. Перезаписываю старые значения новыми (они как раз только что пришли)
		$this->setValues($fieldsData);

		// 2. Сохраняю сырые данные формы
		$this->setMasterValues();
		$this->filter();
		$this->save();
		return $this->validate();
	}

	function setValues($fieldsData) {
		foreach($fieldsData as $name => $value) {
			if (!empty($this->elements[$name])) {
				$this->elements[$name]->setValue($value)->setParital($this->partial);
			}
		}
	}

	public function setMasterValues()
	{
		foreach ($this->elements as $element) {
			/* @var $element \Pinenut\Form\Element */
			if (!$element->isSlave()) {
				continue;
			}

			$element->setMasterValue($this->elements[$element->getMasterName()]->getValue());
		}
	}

	public function save()
	{
		$formModel = $this->getModel();
		foreach ($this->elements as $elementName => $element) {
			$element->isSubmit();

			if ($element->isSubmit()) {
				continue;
			}
			$formModel->formFields[$elementName] = $element->getValue();
		}
		$formModel->save();
	}

	public function flush()
	{
		$f = Model::delete($this->getModel()->_id)->execute();
		$this->formModel = null;
	}

	public function filter()
	{
		foreach ($this->elements as $element) {
			$element->filter();
		}
	}

	public function validate()
	{
		$isValid = true;
		foreach ($this->elements as $element) {
			if (!$element->validate()) {
				$isValid = false;
			}
		}

		return $isValid;
	}

	/**
	 * Населяем форму из модели формы
	 *
	 * @param Model $formModel
	 */
	public function populate(Model $formModel)
	{
		foreach((array)$formModel->formFields as $name => $value) {
			if (!empty($this->formConfig['elements'][$name])) {
				$this->formConfig['elements'][$name]['value'] = $value;
			}
		}
	}

	public function getValues()
	{
		$retval = array();
		foreach ($this->elements as $elementName => $element) {
			$retval[$elementName] = $element->getValue();
		}
		return $retval;
	}

	public function addErrors($element, $errors) {
		$this->elements[$element]->addErrors($errors);
	}
}